/*
Copyright (C) 2010-2011 Sylvain Martin (Spintec - CEA/CNRS/UJF/G-INP)
All rights reserved.

This file is part of the SpinToolBow

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include "SPlot2D.h"

#include "SPlot2DWidget.h"
#include "SPlotCurveData.h"
#include "SData\SData2D.h"

#include <QDialog>
#include <QString>
#include <QVector>
#include <QList>
#include <QIcon>

#include <QToolBar>
#include <QToolButton>

#include <QScriptable>
#include <QScriptValue>
#include <QScriptEngine>

#include <qwt_plot.h>
#include <qwt_plot_canvas.h>
#include <qwt_plot_curve.h>
#include <qwt_symbol.h>
#include <qwt_data.h>
#include <qwt_legend.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \fn SPlot2D::SPlot2D(QWidget *parent)
///
/// \brief	constructor.
///
/// \param parent if not null, the Qt parent
////////////////////////////////////////////////////////////////////////////////////////////////////
SPlot2D::SPlot2D(QWidget *parent /* = NULL */)
	: QMainWindow(parent){
		setupUI();

		// add curves
		curveList_.append(new QwtPlotCurve("Curve 1"));
		dataList_.append(NULL);

		curveList_.at(0)->setPen(QPen(Qt::red));
		curveList_.at(0)->attach(plot2D_);


}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \fn SPlot2D::SPlot2D( SData2D *data, QWidget *parent)
///
/// \brief	constructor.
///
/// \param data a pointer to the data that we want in first curve
/// \param parent if not null, the Qt parent
///
/// \warning do not destroy the data before the complete load of the windows
////////////////////////////////////////////////////////////////////////////////////////////////////
SPlot2D::SPlot2D(SData2D *data, bool overbookDefault/* = true*/ ,QWidget *parent /*= 0*/):
	QMainWindow(parent),
	overbookDefault_(overbookDefault)
{
	setupUI();

	// add curves
	curveList_.append(new QwtPlotCurve("Curve 1"));
	dataList_.append(NULL);

	curveList_.at(0)->setPen(QPen(Qt::red));
	curveList_.at(0)->attach(plot2D_);

	setData(data);
}


////////////////////////////////////////////////////////////////////////////////////////////////////
/// \fn	void SPlot2D::setData(SData2D const &data, int const curveNb)
///
/// \brief	add new points at the end of the actual data, create the curve if necessary .
///
/// \remark Copy the data in a local variable so, you can destroy the Data2D after.
////////////////////////////////////////////////////////////////////////////////////////////////////
void SPlot2D::setData(SData2D *data, int  curveNb){
	double * x = data->xData();
	double * y = data->yData();

	addData(x, y, data->count(),curveNb);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \fn	void SPlot2D::setData(SData2D const data, int const curveNb)
///
/// \brief	add new points at the end of the actual data, create the curve if necessary .
///
/// \remark Copy the data in a local variable so, you can destroy the Data2D after.
////////////////////////////////////////////////////////////////////////////////////////////////////
Q_INVOKABLE void SPlot2D::setData( SData2D  data, int curveNb/*=0*/ )
{
	double * x = data.xData();
	double * y = data.yData();

	addData(x, y, data.count(), curveNb);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \fn	void SPlot2D::addData(double const x, double const y, int const curveNb)
///
/// \brief	add new points at the end of the actual data, create the curve if necessary .
///
/// \sa SPlot2D::append( double x, double y, int const curveNb)
///
/// exactly the same that \c SPlot2D::addData(double  x, double  y, int const curveNb)
////////////////////////////////////////////////////////////////////////////////////////////////////
void SPlot2D::addData(double  x, double  y, int const curveNb){
	addData(&x,&y,1,curveNb);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \fn	void SPlot2D::addData(double const x, double const y, int const curveNb)
///
/// \brief	add new points at the end of the actual data, create the curve if necessary .
///
/// \sa SPlot2D::addData(double  *x, double  *y, int const size, int const curveNb)
///
/// exactly the same that \c SPlot2D::addData(double  x, double  y, int const curveNb)
////////////////////////////////////////////////////////////////////////////////////////////////////
Q_INVOKABLE void SPlot2D::append( double x, double y, int const curveNb/*=0*/ )
{
	addData(  x,   y, curveNb);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \fn	void SPlot2D::addData(double const x, double const y, int const curveNb)
///
/// \brief	add new points at the end of the actual data, create the curve if necessary .
///
////////////////////////////////////////////////////////////////////////////////////////////////////
void SPlot2D::addData(double  *x, double  *y, int const size, int const curveNb){

	
	//if this index exists, but it is null, we replace it
	if ( dataList_.size() >= curveNb ){	

		if( dataList_.at(curveNb) == NULL ){
			dataList_.replace(curveNb,new SPlotCurveData);
			
		}
	}else{
		// if the index does not exist, we fill the tab with NULL until the good index
		for(int i=dataList_.size(); i<curveNb; i++){
			dataList_.append(NULL);}

		dataList_.append(new SPlotCurveData);

	}
	//if this index exists, but it is null, we replace it
	if (curveList_.size() >= curveNb ){

		if( curveList_.at(curveNb) == NULL){
			
			curveList_.replace(curveNb, new QwtPlotCurve("Curve" + QString::number(curveNb)));
			curveList_.at(curveNb)->attach(plot2D_);
		}
		
	}else{
		// if the index does not exist, we fill the tab with NULL until the good index
		for(int i=dataList_.size(); i<curveNb; i++){
			curveList_.append(NULL);
		}

		curveList_.append(new QwtPlotCurve("Curve" + QString::number(curveNb)));


	}

	// if the overbookDefault_ has changed
	dataList_.at(curveNb)->setOverbook(overbookDefault_);

	// we append the data in the data list dataList_
	dataList_.at(curveNb)->append(x, y, size);


	int Mycount = dataList_.at(curveNb)->count();

	// we link the data and the plot
	curveList_.at(curveNb)->setRawData(dataList_.at(curveNb)->x(),
		dataList_.at(curveNb)->y(), dataList_.at(curveNb)->count());

	// we repaint the last points
	//plot2D_->replot();
	const bool cacheMode = 
		plot2D_->canvas()->testPaintAttribute(QwtPlotCanvas::PaintCached); // strange hack ...

	plot2D_->canvas()->setPaintAttribute(QwtPlotCanvas::PaintCached, false);
	curveList_.at(curveNb)->draw(curveList_.at(curveNb)->dataSize() - size, curveList_.at(curveNb)->dataSize() - 1);
	plot2D_->canvas()->setPaintAttribute(QwtPlotCanvas::PaintCached, cacheMode);
	


}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \fn	SPlot2D::~SPlot2D()
///
/// \brief	Destructor.
///
////////////////////////////////////////////////////////////////////////////////////////////////////

SPlot2D::~SPlot2D(){

	for(int i=0; i < curveList_.count(); i++){
		delete(curveList_.value(i));
		delete(dataList_.value(i));
	}


}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \fn	SPlot2DWidget * SPlot2D::plot2DWidget(void)
///
/// \brief	Gets the SPlot2DWidget.
///
////////////////////////////////////////////////////////////////////////////////////////////////////

SPlot2DWidget * SPlot2D::plot2DWidget(void){
	return plot2D_;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \fn	QScriptValue SData2DScript::qsCtor(QScriptContext *context, QScriptEngine *engine)
///
/// \brief	this function is use by script engine to create a new SPlot2D object.
///
/// \author	Sylvain Martin
/// \date	1/28/2011
///
/// \param [in,out]	context	If non-null, the QScript context.
/// \param [in,out]	engine	If non-null, the QScript engine.
///
/// \return	a QScript version of a SData2D.
////////////////////////////////////////////////////////////////////////////////////////////////////

QScriptValue SPlot2D::qsCtor(QScriptContext *context, QScriptEngine *engine) {
	 QWidget *parent;
	 QString name;

	 if(context->argument(0).isObject()){
			parent = qobject_cast<QWidget *>(context->argument(0).toQObject());
	 }else{
			 parent = NULL;
	 }

     QObject *object = new SPlot2D(parent);
     return engine->newQObject(object, QScriptEngine::ScriptOwnership);
 }

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \fn	void SPlot2D::setupUI(void)
///
/// \brief	Sets up the user interface.
///
////////////////////////////////////////////////////////////////////////////////////////////////////

void SPlot2D::setupUI(void){
	this->setMinimumSize(200,200);
	this->setGeometry(200,200,300,300);
	this->setObjectName("Plot 2D");



	plot2D_ = new SPlot2DWidget(this);
	this->setCentralWidget(plot2D_);
	plot2D_->insertLegend(new QwtLegend(), QwtPlot::BottomLegend);

	 toolBar = new QToolBar(this);

	//le bouton zoom
	 QString	txtZoom =  "Zoom :\nLeftButton for the zooming \n";
				txtZoom += "MidButton for the panning \n";
				txtZoom += "RightButton: zoom out by 1 \n";
				txtZoom += "Ctrl+RighButton: zoom out to full size";

	 actZoom = new QAction("&Zoom",this);
	 actZoom->setIcon(QIcon(":icon/zoom.png"));
	 actZoom->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Z));
	 actZoom->setToolTip(txtZoom);
	 toolBar->addAction(actZoom);
	 connect(actZoom, SIGNAL(triggered()),this->plot2D_, SLOT(zoom()));

	 //le bouton autoscale
	 actAutoScale = new QAction("&Auto Scale",this);
	 actAutoScale->setIcon(QIcon(":icon/autoscale.png"));
	 actAutoScale->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_A));
	// actAutoScale->setToolTip("");
	 toolBar->addAction(actAutoScale);
	 connect(actAutoScale, SIGNAL(triggered()),this->plot2D_, SLOT(autoScale()));

	  //le bouton autoscale
	 actSetScale = new QAction("&Set Scale",this);
	 actSetScale->setIcon(QIcon(":icon/setsacle.png"));
	 actSetScale->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
	// actSetScale->setToolTip("");
	 toolBar->addAction(actSetScale);
	 connect(actSetScale, SIGNAL(triggered()),this->plot2D_, SLOT(selectScale()));

	// on met la bar d'outil en place
	addToolBar(toolBar);

	//we put an icon
	QIcon icon;
	icon.addFile(QString::fromUtf8(":/compass.ico"), QSize(), QIcon::Normal, QIcon::Off);
	this->setWindowIcon(icon);

}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \fn	void SPlot2D::setTitle( QString title, int curveNb)
///
/// \brief	(SCRIPTABLE) Set the title of curve curveNb.
///
/// \param title the title
/// \param curveNb number of the curve
////////////////////////////////////////////////////////////////////////////////////////////////////
Q_INVOKABLE void SPlot2D::setTitle( QString title, int curveNb/*=0*/ )
{
	curveList_.at(curveNb)->setTitle(title);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \fn	QString SPlot2D::title()
///
/// \brief	(SCRIPTABLE) Get the title of curve curveNb.
///
/// \param title the title
/// \param curveNb number of the curve
////////////////////////////////////////////////////////////////////////////////////////////////////
Q_INVOKABLE QString SPlot2D::title(int curveNb/*=0*/ )
{
	return curveList_.at(curveNb)->title().text();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \fn	QString SPlot2D::title()
///
/// \brief	(SCRIPTABLE) Get the title of the curve curveNb.
///
/// \param title the title
/// \param curveNb number of the curve
////////////////////////////////////////////////////////////////////////////////////////////////////
Q_INVOKABLE void SPlot2D::clearData( int curveNb /*=0*/ )
{
	if ( dataList_.size() >= curveNb && dataList_.at(curveNb) != NULL ){
		delete dataList_.at(curveNb);
		dataList_.replace(curveNb,NULL);
	}
	if (curveList_.size() >= curveNb && curveList_.at(curveNb) != NULL){
		delete curveList_.at(curveNb);
		curveList_.replace(curveNb,NULL);
	}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \fn	QString SPlot2D::title()
///
/// \brief	(SCRIPTABLE) set the color of the curve curveNb.
///
/// \param name the name of the color
/// \param curveNb number of the curve
///
/// Sets the RGB value of this curve to name, which may be in one of these formats:
///
/// \li #RGB (each of R, G, and B is a single hex digit)
/// \li #RRGGBB
/// \li #RRRGGGBBB
/// \li #RRRRGGGGBBBB
/// \li A name from the list of colors defined in the list of SVG color keyword 
///     names provided by the World Wide Web Consortium; for example, "steelblue" or 
///		"gainsboro". These color names work on all platforms.
/// \li transparent - representing the absence of a color.
///
////////////////////////////////////////////////////////////////////////////////////////////////////
Q_INVOKABLE void SPlot2D::setColor( QString name, int curveNb /*=0*/ )
{
	if(dataList_.size() >= curveNb && dataList_.at(curveNb) != NULL){
		QColor color;
		color.setNamedColor(name);
		curveList_.at(curveNb)->setPen(QPen(color));
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \fn	bool SPlotCurveData::overbook(int curveNb) const
///
/// \brief	return the overbook parameter of the curve curveNb 
///
/// \param curveNb the number of the curve, if it is not valid, the function just return false
///
/// \remark true by default
/// 
/// if overbook is true, reserve more memory while reallocating memory for the 
/// AddPoint function (process is accelerated because there is no reallocated each time) but add invisible 
/// point in the curve and maybe offset the scale.
///
/// \sa SPlot2D::setOverbook( bool val, int curveNb)
////////////////////////////////////////////////////////////////////////////////////////////////////
Q_INVOKABLE bool SPlot2D::overbook( int curveNb /*=0*/ ) const{
	if ( dataList_.size() >= curveNb && dataList_.at(curveNb) != NULL ){
		return dataList_.at(curveNb)->overbook();

	}else{
		return false;
	}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \fn	void SPlotCurveData::setOverbook( bool val, int curveNb /*=0*/)
///
/// \brief	set the overbook parameter of the curve curveNb 
///
/// \param curveNb the number of the curve, if it is not valid, the function just return false
///
/// \remark true by default
/// 
/// if overbook is true, reserve more memory while reallocating memory for the 
/// AddPoint function (process is accelerated because there is no reallocated each time) but add invisible 
/// point in the curve and maybe offset the scale.
///
/// \sa SPlot2D::overbook( int curveNb)
////////////////////////////////////////////////////////////////////////////////////////////////////
Q_INVOKABLE void SPlot2D::setOverbook( bool val, int curveNb /*=0*/ ){

	if ( dataList_.size() >= curveNb && dataList_.at(curveNb) != NULL ){
		 dataList_.at(curveNb)->setOverbook(val);
	}

}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \fn	bool SPlot2D::overbookDefault() const
///
/// \brief	return the overbook parameter use by default for this window
///
/// \sa SPlot2D::overbook( int curveNb)
/// \sa SPlot2D::setOverbook( bool val, int curveNb)
/// \sa SPlot2D::setOverbookDefault( bool val )
////////////////////////////////////////////////////////////////////////////////////////////////////
Q_INVOKABLE bool SPlot2D::overbookDefault() const
{
	return overbookDefault_;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \fn	bool SPlot2D::overbookDefault() const
///
/// \brief	set the overbook parameter use by default for this window
///
/// \sa SPlot2D::overbook( int curveNb)
/// \sa SPlot2D::setOverbook( bool val, int curveNb)
/// \sa SPlot2D::overbookDefault()
////////////////////////////////////////////////////////////////////////////////////////////////////
Q_INVOKABLE void SPlot2D::setOverbookDefault( bool val )
{
	overbookDefault_ = val;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \fn	void SPlot2D::zoom(bool on)
///
/// \brief	(SLOT) actives or deactivates the zooms function.
///
///
///  LeftButton for the zooming
///  MidButton for the panning
///  RightButton: zoom out by 1
///  Ctrl+RighButton: zoom out to full size
////////////////////////////////////////////////////////////////////////////////////////////////////
void SPlot2D::zoom()
{
	plot2DWidget()->zoom();
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \fn	void SPlot2D::autoScale(bool on)
///
/// \brief	(SLOT) Reset the scale with the autoScale.
///
////////////////////////////////////////////////////////////////////////////////////////////////////

void SPlot2D::autoScale(){
	plot2DWidget()->autoScale();
}


////////////////////////////////////////////////////////////////////////////////////////////////////
/// \fn	void SPlot2D::selectScale()
///
/// \brief	(SLOT) Open a dialog box asking the scale.
///
////////////////////////////////////////////////////////////////////////////////////////////////////

void SPlot2D::selectScale(){
	plot2DWidget()->selectScale();
}


////////////////////////////////////////////////////////////////////////////////////////////////////
/// \fn	void SPlot2D::setScale(double Xmin, double Xmax, double Ymin, double Ymax)
///
/// \brief	(SLOT) sets the scales selected by the user.
///
/// \param Xmin the maximum of the x scale
/// \param Xmax the minimum of the x scale
/// \param Ymin the maximum of the y scale
/// \param Ymax the minimum of the y scale
////////////////////////////////////////////////////////////////////////////////////////////////////
void SPlot2D::setScale(double Xmin, double Xmax, double Ymin, double Ymax){
	plot2DWidget()->setScale( Xmin,  Xmax,  Ymin,  Ymax);
}